package com.ruoyi.web.webSocket.util;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.ruoyi.web.webSocket.Pojo.SocketConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArraySet;

/**
 * WebSocket 服务器端点处理实时通信。
 * 端点 URL 为 "/websocket/{nickname}"，其中 {nickname} 是用户的昵称。
 */
@ServerEndpoint(value = "/websocket/{nickname}")
@Component
@Slf4j
public class MyWebsocket {
    // 存储会话 ID 和会话对象的映射。
    private static Map<String, Session> map = new HashMap<>();

    // 线程安全的集合，用于管理所有连接的 WebSocket 客户端。
    private static CopyOnWriteArraySet<MyWebsocket> clients = new CopyOnWriteArraySet<>();

    // 当前 WebSocket 连接的会话对象。
    private Session session;

    // 用户的昵称。
    private String nickname;

    /**
     * 处理新的 WebSocket 连接。
     * @param session WebSocket 会话对象。
     * @param nickname WebSocket URL 中提供的昵称。
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("nickname") String nickname){
        this.session = session;
        this.nickname = nickname;

        // 将新会话添加到映射和客户端集合中。
        map.put(session.getId(), session);
        clients.add(this);

        // 记录新连接并通知用户。
        log.info("有新用户加入,当前人数为：{}", clients.size());
        this.session.getAsyncRemote().sendText(nickname + "已成功连接(其频道号为：" + session.getId() + "),当前在线人数为：" + clients.size());
    }

    /**
     * 处理 WebSocket 断开连接。
     */
    @OnClose
    public void onClose(){
        // 从客户端集合中移除会话。
        clients.remove(this);
        log.info("有用户断开连接,当前人数为：{}", clients.size());
    }

    /**
     * 处理传入的 WebSocket 消息。
     * @param message 从客户端收到的消息。
     * @param session WebSocket 会话对象。
     * @param nickname 发送者的昵称。
     */
    @OnMessage
    public void onMessage(String message, Session session, @PathParam("nickname") String nickname) {
        log.info("来自客户端：{}发来的消息：{}", nickname, message);

        ObjectMapper objectMapper = new ObjectMapper();

        try {
            // 将传入的消息解析为 JsonNode 以方便调试和检查格式。
            JsonNode jsonNode = objectMapper.readTree(message);

            // 记录 JSON 消息内容
            log.info("接收到的 JSON 消息：{}", jsonNode.toString());

            // 检查 JSON 消息是否包含必需的字段
            if (jsonNode.has("type") && jsonNode.has("msg") && jsonNode.has("toUser")) {
                // 将 JSON 解析为 SocketConfig 对象
                SocketConfig socketConfig = objectMapper.treeToValue(jsonNode, SocketConfig.class);

                if (socketConfig.getType() == 1) {  // 处理私聊消息
                    socketConfig.setFromUser(session.getId());
                    Session fromSession = map.get(socketConfig.getFromUser());
                    Session toSession = map.get(socketConfig.getToUser());

                    if (toSession != null) {  // 接收者存在，发送消息给发送者和接收者
                        fromSession.getAsyncRemote().sendText(nickname + "：" + socketConfig.getMsg());
                        toSession.getAsyncRemote().sendText(nickname + "：" + socketConfig.getMsg());
                    } else {  // 接收者不存在，通知发送者
                        fromSession.getAsyncRemote().sendText("频道号不存在或对方不在线");
                    }
                } else {  // 处理群聊消息
                    broadcast(nickname + "：" + socketConfig.getMsg());
                }
            } else {
                log.error("JSON 消息缺少必需字段：{}", message);
            }
        } catch (JsonMappingException e) {
            log.error("JSON 映射错误：{}", e.getMessage());
        } catch (JsonProcessingException e) {
            log.error("JSON 处理错误：{}", e.getMessage());
        } catch (IOException e) {
            log.error("I/O 错误：{}", e.getMessage());
        }
    }


    /**
     * 处理 WebSocket 错误。
     * @param session WebSocket 会话对象。
     * @param error 错误信息。
     */
    @OnError
    public void onError(Session session, Throwable error){
        log.error("出现错误");
        error.printStackTrace();
    }

    /**
     * 向所有连接的 WebSocket 客户端广播消息。
     * @param message 要广播的消息。
     */
    public void broadcast(String message){
        for (MyWebsocket websocket : clients){
            // 异步发送消息给每个客户端。
            websocket.session.getAsyncRemote().sendText(message);
        }
    }
}
